#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include "include/utils.h"

/* Constant Declarations */
char      Look;                 // Lookahead Character
int       LCount;               // Label Counter
const int buf_len = 64;

/* Read New Character From Input Stream */
void GetChar() {
    Look = getchar();
}

/* Report an Error */
void WriteLn(const char* s) {
    if (s) {
        printf(s);
    }

    printf("\n");
}

void Error(const char* s) {
    WriteLn(0);

    char buf[buf_len] = {0};
    sprintf(buf, "Error: %s.", s);
    WriteLn(buf);
}

/* Report Error and Halt */
void Abort(const char* s) {
    Error(s);
    exit(0);
}

/* Report What Was Expected */
void Expected(const char* s) {
    char buf[buf_len] = {0};
    sprintf(buf, "%s Expected", s);
    Abort(buf);
}

/* Match a Specific Input Character */
void Match(char x) {
    if (Look == x) {
        GetChar();
    }
    else {
        char buf[buf_len] = {0};
        sprintf(buf, "'''' %c ''''", x);
        Expected(buf);
    }
}

/* Recognize an Alpha Character  */
int IsAlpha(char c) {
    return isalpha(c);
}

/* Recognize a Decimal Digit */
int IsDigit(char c) {
    return isdigit(c);
}

/* Recognize an Addop */
int IsAddop(char c) {
    if ((c == '+') ||
        (c == '-')) {
        return 1;
    }
    else {
        return 0;
    }
}

/* Get an Identifier */
char GetName() {
    if (!IsAlpha(Look)) {
        Expected("Name");
    }

    char result = Look;
    GetChar();
    return result;
}

/* Get a Number */
char GetNum() {
    if (!IsDigit(Look)) {
        Expected("Integer");
    }

    char result = Look;
    GetChar();
    return result;
}

/* Output a String with Tab */
void EmitLn(const char* s) {
    printf("\t %s\n", s);
}

/* Recognize and Translate an "Other" */
void Other() {
    char buf[buf_len]={0};
	sprintf(buf, "%c", GetName());
	EmitLn(buf);
}

/* Initialize */
void Init() {
    GetChar();
    LCount = 0;
}

/* Generate a Unique Label */
char* NewLabel() {
    CharBuf cb;
    charbuf_init(&cb, 16, 8);

    char buf[buf_len];
    sprintf(buf, "L%d", LCount);
    charbuf_append_str(&cb, buf);
    
    LCount++;

    return cb.buf;
}

/* Pose a Label To Output */
void PostLabel(const char* L) {
    printf("%s:\n", L);
}

/* Parse and Translate a Boolean Condition */
/* This version is a dummy */
void Condition() {
    EmitLn("<condition>");
}

void Block(const char*); // Forward declaration

/* Recognize and Translate an IF Construct */
void DoIf(const char* L) {
    char* L1, *L2;
    Match('i');
    Condition();
    L1=NewLabel();
    L2=L1;
    printf("\t BEQ %s\n", L1);
    Block(L);
    if (Look == 'l') {
        Match('l');
        L2=NewLabel();
        printf("\t BRA %s\n", L2);
        PostLabel(L1);
        Block(L);
    }
    Match('e');
    PostLabel(L2);
    free(L1);
    if (L1 != L2) {
        free(L2);
    }
}

/* Parse and Translate a WHILE Statement */
void DoWhile() {
    char *L1, *L2;
    Match('w');
    L1=NewLabel();
    L2=NewLabel();
    PostLabel(L1);
    Condition();
    printf("\t BEQ %s\n", L2);
    Block(L2);
    Match('e');
    printf("\t BRA %s\n", L1);
    PostLabel(L2);
    free(L1);
    free(L2);
}

/* Parse and Translate a LOOP Statement */
void DoLoop() {
    char *L1, *L2;
    Match('p');
    L1=NewLabel();
    L2=NewLabel();
    PostLabel(L1);
    Block(L2);
    Match('e');
    printf("\t BRA %s\n", L1);
    PostLabel(L2);
    free(L1);
    free(L2);
}

/* Parse and Translate a REPEAT Statement */
void DoRepeat() {
    char *L, *Exit;
    Match('r');
    L=NewLabel();
    Exit=NewLabel();
    PostLabel(L);
    Block(Exit);
    Match('u');
    Condition();
    printf("\t BEQ %s\n", L);
    PostLabel(Exit);
    free(L);
    free(Exit);
}

/* Parse and Translate an Expression */
/* This version is a dummy */
void Expression() {
    EmitLn("<expr>");
}

/* Parse and Translate a FOR Statement */
void DoFor() {
    char *L1, *L2;
    char Name;

    Match('f');
    L1=NewLabel();
    L2=NewLabel();
    Name=GetName();
    Match('=');
    Expression();
    EmitLn("SUBQ #1, D0");
    printf("\t LEA %c(PC),A0\n", Name);
    EmitLn("MOVE D0,(A0)");
    Expression();
    EmitLn("MOVE D0,-(SP)");
    PostLabel(L1);
    printf("\t LEA %c(PC),A0\n", Name);
    EmitLn("MOVE (A0), D0");
    EmitLn("ADDQ #1, D0");
    EmitLn("MOVE D0, (A0)");
    EmitLn("CMP (SP), D0");
    printf("\t BGT %s\n", L2);
    Block(L2);
    Match('e');
    printf("\t BRA %s\n", L1);
    PostLabel(L2);
    EmitLn("ADDQ #2, SP");
    free(L1);
    free(L2);
}

/* Parse and Translate a DO Statement */
void DoDo() {
    char *L1, *L2;

    Match('d');
    L1=NewLabel();
    L2=NewLabel();
    Expression();
    EmitLn("SUBQ #1,D0");
    PostLabel(L1);
    EmitLn("MOVE D0,-(SP)");
    Block(L2);
    EmitLn("MOVE (SP)+,D0");
    printf("\t DBRA D0,%s\n", L1);
    EmitLn("SUBQ #2,SP");
    PostLabel(L2);
    EmitLn("ADDQ #2,SP");
    free(L1);
    free(L2);
}

/* Recognize and Translate a BREAK */
void DoBreak(const char* L) {
    Match('b');
    if (L) {
        printf("\t BRA %s\n", L);
    }
    else {
        Abort("No loop to break from");
    }
}

/* Recognize and Translate a Statement Block */
void Block(const char* L) {
    while (Look != 'e' &&
           Look != 'l' &&
           Look != 'u') {
        switch (Look) {
        case 'i': DoIf(L); break;
        case 'w': DoWhile(); break;
        case 'p': DoLoop(); break;
        case 'r': DoRepeat(); break;
        case 'f': DoFor(); break;
        case 'd': DoDo(); break;
        case 'b': DoBreak(L); break;
        default: Other(); break;
        }
    }
}

/* Parse and Translate a Program */
void DoProgram() {
    Block(0);
    if (Look != 'e') {
        Expected("End");
    }
    EmitLn("END");
}

/* Main Program */
int main(int argc, char** argv) {
    Init();
    DoProgram();
}
